from time import *

import logging
import logging.config
import ma.const
import ma.log

from .constants import *
 
class ExpireUnresponsiveTasks(object):
   
    """this class will mark tasks running at a node tracker
    that have not responded in X sec. these tasks are placed in the failed tasks
    dict 
    """
    
    def __init__(self, nodetracker, check_task_expiry_interval):
        
        self.nodetracker = nodetracker
        
        self.check_task_expiry_interval = check_task_expiry_interval
        
        #add the timer that will on expiry call checkTasksExpiry
        self.nodetracker.timer.addTimer(self.check_task_expiry_interval, self.checkTasksExpiry,[])
        
        #initializing the logger
        self.__log = ma.log.get_logger('ma.commons')
                
           
    def checkTasksExpiry(self): 
        
        """this func will check the running tasks dict for tasks that have not sent a ping 
        in X sec and therefore are considered to have expired and moves them to the failed 
        tasks dict 
        """
        failed_maptasks_list = []
        failed_reducetasks_list = []
        #checking the running map tasks dict : for loop outer dict with job_ids
        for i in self.nodetracker.running_map_tasks:
            
            #for loop outer dict with task_ids
            for j in self.nodetracker.running_map_tasks[i]:
                
                #time interval for a ping
                a = ((self.nodetracker.tasks_last_ping[i])[j])[1]
                b = time()
                time_gap =  b - a
                               
                #if interval for ping exceeds expiry then remove task from
                #running tasks and add to failed tasks dict
                if(time_gap > ma.const.XmlData.get_float_data(ma.const.xml_nt_task_expiry_interval)):
                    
                    #lock the TIP in the dict that is being changed after checking if TIP in running tasks
                                        
                    failed_maptasks_list.append((i,j))
                    
                    self.__log.debug("Task with task_id %d" %j) 
                    self.__log.debug("and job_id %d failed" %i)  
        #checking reduces for expired ping times
        for i in self.nodetracker.running_reduce_tasks:
            
            #for loop outer dict with task_ids
            for j in self.nodetracker.running_reduce_tasks[i]:
                
                #time interval for a ping
                a = ((self.nodetracker.tasks_last_ping[i])[j])[1]
                b = time()
                time_gap =  b - a
                               
                #if interval for ping exceeds expiry then remove task from
                #running tasks and add to failed tasks dict
                if(time_gap > ma.const.XmlData.get_float_data(ma.const.xml_nt_task_expiry_interval)):
                    
                    #lock the TIP in the dict that is being changed after checking if TIP in running tasks
                                        
                    failed_reducetasks_list.append((i,j))
                    
                    self.__log.debug("Task with task_id %d" %j) 
                    self.__log.debug("and job_id %d failed" %i)  
                
        for index in range (0,len(failed_maptasks_list)):
                    
            a = failed_maptasks_list[index][0]
            b = failed_maptasks_list[index][1]
            #del (self.nodetracker.running_tasks[a])[b]
            
            #lock the TIP in the dict that is being changed after checking if TIP in running tasks
            self.nodetracker.acquireTIPLock(self.nodetracker.running_map_tasks, a, b)
            
            if a not in self.nodetracker.failed_map_tasks:
                self.nodetracker.failed_map_tasks[a]= {b: (self.nodetracker.running_map_tasks[a]).pop(b)}
                
            else:                            
               # pop task from running tasks and add to failed tasks
               (self.nodetracker.failed_map_tasks[a])[b] = (self.nodetracker.
                                                                running_map_tasks[a]).pop(b)
                
            #find out if task was a map or reduce
            map_or_reduce = (self.nodetracker.failed_map_tasks[a])[b].getMapOrReduce()
            
            #called taskFailed method of nodetracker for every task that this class expires for being
            #unresponsive
            self.nodetracker.taskFailed(a,b, map_or_reduce)
                                                                     
            #lock was acquired on a running tasks TIP but released on a failed tasks TIP
            #cause the TIP is popped from running tasks and assigned to failed tasks
            self.nodetracker.releaseTIPLock(self.nodetracker.failed_map_tasks, a, b)                    
                                       
        while index in range (0,len(failed_reduce_tasks_list)):
                    
            a = failed_reducetasks_list[index][0]
            b = failed_reducetasks_list[index][1]
            #del (self.nodetracker.running_tasks[a])[b]
            
            #lock the TIP in the dict that is being changed after checking if TIP in running tasks
            self.nodetracker.acquireTIPLock(self.nodetracker.running_map_tasks, a, b)
            
            if a not in self.nodetracker.failed_reduce_tasks:
                self.nodetracker.failed_reduce_tasks[a]= {b: (self.nodetracker.running_map_tasks[a]).pop(b)}
                
            else:                            
               # pop task from running tasks and add to failed tasks
               (self.nodetracker.failed_reduce_tasks[a])[b] = (self.nodetracker.
                                                                running_map_tasks[a]).pop(b)
                
            #find out if task was a map or reduce
            map_or_reduce = (self.nodetracker.failed_reduce_tasks[a])[b].getMapOrReduce()
            
            #called taskFailed method of nodetracker for every task that this class expires for being
            #unresponsive
            self.nodetracker.taskFailed(a,b, map_or_reduce)
                                                                     
            #lock was acquired on a running tasks TIP but released on a failed tasks TIP
            #cause the TIP is popped from running tasks and assigned to failed tasks
            self.nodetracker.releaseTIPLock(self.nodetracker.failed_reduce_tasks, a, b)                    
                      
        